package org.vacoor.nothing.security.nls;

import java.sql.Driver;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by Administrator on 2015/3/2.
 */
public class TranslatorManager {
    static {
        loadInitialDrivers();
        // println("Language DriverManager initialized");
    }

    private static void loadInitialDrivers() {
        String translators = System.getProperty("language.translators");

        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()

        ServiceLoader<Translator> loadedDrivers = ServiceLoader.load(Translator.class);
        Iterator driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
        try {
            while (driversIterator.hasNext()) {
                driversIterator.next();
            }
        } catch (Throwable t) {
            // Do nothing
        }

        println("DriverManager.initialize: jdbc.drivers = " + translators);

        if (null == translators || "".equals(translators)) {
            return;
        }
        String[] driversList = translators.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true, ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

    public static void println(String message) {
        /*
        synchronized (logSync) {
            if (logWriter != null) {
                logWriter.println(message);

                // automatic flushing is never enabled, so we must do it ourselves
                logWriter.flush();
            }
        }
        */
        System.out.println(message);
    }

    private static Translator getTranslator(String url, Properties props) {
        TranslationException reason = null;

        for (DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            ClassLoader callerCL = null;
            if (isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Translator con = null;
                    // Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                    throw new TranslationException();
                } catch (TranslationException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null) {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for " + url);
        // throw new TranslationException("No suitable driver found for " + url, "08001");
        throw new TranslationException("");
    }

    // Indicates whether the class object that would be created if the code calling
    // DriverManager is accessible.
    private static boolean isDriverAllowed(Driver driver, Class<?> caller) {
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        return isDriverAllowed(driver, callerCL);
    }

    // 如果类加载器不同则不允许
    private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
        boolean result = false;
        if (driver != null) {
            Class<?> aClass = null;
            try {
                aClass = Class.forName(driver.getClass().getName(), true, classLoader);
            } catch (Exception ex) {
                result = false;
            }

            result = (aClass == driver.getClass()) ? true : false;
        }

        return result;
    }

    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();

    /**
     * Registers the given driver with the <code>DriverManager</code>.
     * A newly-loaded driver class should call
     * the method <code>registerDriver</code> to make itself
     * known to the <code>DriverManager</code>.
     *
     * @param driver the new JDBC Driver that is to be registered with the
     *               <code>DriverManager</code>
     * @throws java.sql.SQLException if a database access error occurs
     */
    public static synchronized void registerDriver(java.sql.Driver driver)
            throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if (driver != null) {
            // registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

    /*
     * Wrapper class for registered Drivers in order to not expose Driver.equals()
     * to avoid the capture of the Driver it being compared to as it might not
     * normally have access.
     */
    class DriverInfo {

        final Driver driver;

        DriverInfo(Driver driver) {
            this.driver = driver;
        }

        public boolean equals(Object other) {
            return (other instanceof DriverInfo)
                    && this.driver == ((DriverInfo) other).driver;
        }

        public int hashCode() {
            return driver.hashCode();
        }

        public String toString() {
            return ("driver[className=" + driver + "]");
        }
    }
}
